
// Used by below to enforce
SudokuGrid.prototype.strategyGeneric_SubsetEnforcer = function (cache, setter, bitstoclear, ignorelist)
{
	var result;
	for (var i = 0; i < 9; i++)
	{
		if ((1 << i) & ~ignorelist)
		{
			result = cache[i] & ~bitstoclear;
			if (result !== cache[i]) setter(i, result);
		}
	}
}

// this is a generic function that performs the Naked Pairs strategy on a scope (row/col/box). 
// It takes two functions as parameter: getter and setter. 
// Due to the fact that there are congruences between Naked Pairs, Hidden Pairs, and X-Wing, 
// all three are performed by this function by passing different getter setter functions to it. 
SudokuGrid.prototype.strategyGeneric_SubsetEngine = function (getter, setter)
{
	var UnsolvedCount = 0; // UnsolvedCount records the number of unsolved cells in a row/col/box
	var CellsToCheck = [], CandidateCount, cache = [];

	// compile a list of cell to check to reduce calculations later
	for (var i = 0; i < 9; i++)
	{
		cache[i] = getter(i);
		CandidateCount = cache[i].bitCount();
		if (1 < CandidateCount)
		{
			UnsolvedCount++;
			// only process cells with fewer than 5 candidates, as we only check for 4 cells at a time. 
			if (CandidateCount < 5) CellsToCheck.push(i);
		}
	}
	// If there are only 1 or 2 unsolved cells in the row/col/box then this technique has no use. 
	if (UnsolvedCount < 3) return;

	var NumOfCellsToCheck = CellsToCheck.length;
	for (var i = 0; i < NumOfCellsToCheck; i++) // depth 1
	{
		for (var j = i + 1; j < NumOfCellsToCheck; j++) // depth 2 - Double
		{
			var DoubleCandidates = cache[CellsToCheck[i]] | cache[CellsToCheck[j]];
			CandidateCount = DoubleCandidates.bitCount();

			// Shrug off non-doubles
			if (CandidateCount === 2)
			{
				// console.log('Pairs : ' + CellsToCheck[i] + CellsToCheck[j] + ' ' + DoubleCandidates.toString(2));

				// Enforce double
				this.strategyGeneric_SubsetEnforcer(cache, setter, DoubleCandidates, (1 << CellsToCheck[i]) | (1 << CellsToCheck[j]));
			}
			else if (CandidateCount < 5 && UnsolvedCount > 3)
			for (var k = j + 1; k < NumOfCellsToCheck; k++) // depth 3 - Triple
			{
				var TripleCandidates = DoubleCandidates | cache[CellsToCheck[k]];
				CandidateCount = TripleCandidates.bitCount();

				// Shrug off non-triples
				if (CandidateCount === 3)
				{
					// console.log('Triple: ' + CellsToCheck[i] + CellsToCheck[j] + CellsToCheck[k] + ' ' + TripleCandidates.toString(2));

					// Enforce triple
					this.strategyGeneric_SubsetEnforcer(cache, setter, TripleCandidates, (1 << CellsToCheck[i]) | (1 << CellsToCheck[j]) | (1 << CellsToCheck[k]));
				}
				else if (CandidateCount < 5 && UnsolvedCount > 4)
				for (var l = k + 1; l < NumOfCellsToCheck; l++) // depth 4 - Quad
				{
					var QuadCandidates = TripleCandidates | cache[CellsToCheck[l]];
					CandidateCount = QuadCandidates.bitCount();

					// Shrug off non-quads
					if (CandidateCount === 4)
					{
						// console.log('Quads : ' + CellsToCheck[i] + CellsToCheck[j] + CellsToCheck[k] + CellsToCheck[l] + ' ' + QuadCandidates.toString(2));

						// Enforce quad
						this.strategyGeneric_SubsetEnforcer(cache, setter, QuadCandidates, (1 << CellsToCheck[i]) | (1 << CellsToCheck[j]) | (1 << CellsToCheck[k]) | (1 << CellsToCheck[l]));
					}
				}
			}
		}
	}
}

// Call the Naked Pairs strategies on the grid.
SudokuGrid.prototype.strategyNakedSubset = function ()
{
	var getter, setter, changed = false;

	console.log('=== Naked Pairs ===');
	for (var i = 0; i < 9; i++)
	{
		// row
		getter = function (thisObj, fixed) { return function (col) { return thisObj.notations[fixed][col]; }; } (this, i);
		setter = function (thisObj, fixed) { return function (col, value) { changed = true; thisObj.setNotation(fixed, col, value); }; } (this, i);
		this.strategyGeneric_SubsetEngine(getter, setter);
	
		// column
		getter = function (thisObj, fixed) { return function (row) { return thisObj.notations[row][fixed]; }; } (this, i);
		setter = function (thisObj, fixed) { return function (row, value) { changed = true; thisObj.setNotation(row, fixed, value); }; } (this, i);
		this.strategyGeneric_SubsetEngine(getter, setter);

		// box
		getter = function (thisObj, fixed) { return function (pos) { return thisObj.getNotationBox(fixed, pos); }; } (this, i);
		setter = function (thisObj, fixed) { return function (pos, value) { changed = true; thisObj.setNotationBox(fixed, pos, value); }; } (this, i);
		this.strategyGeneric_SubsetEngine(getter, setter);
	}
	return changed;
}

// Call the Hidden Pairs strategies on the grid.
// This function uses the fact that, Naked Pairs and Hidden Pairs are computationally identical.
// Naked pairs expects array format to be:    scope[position] = candidate;
// Hidden Pairs expects array format to be:   scope[candidate] = position;
SudokuGrid.prototype.strategyHiddenSubset = function ()
{
	var getter, setter, changed = false;

	console.log('=== Hidden Pairs ===');

	for (var i = 0; i < 9; i++)
	{
		// row
		getter = function (thisObj, fixed) { return function (digit) { return thisObj.getPositionsInRow((1 << digit), fixed); }; } (this, i);
		setter = function (thisObj, fixed) { return function (digit, value) { changed = true; thisObj.setPositionsInRow((1 << digit), fixed, value); }; } (this, i);
		this.strategyGeneric_SubsetEngine(getter, setter);
	
		// column
		getter = function (thisObj, fixed) { return function (digit) { return thisObj.getPositionsInCol((1 << digit), fixed); }; } (this, i);
		setter = function (thisObj, fixed) { return function (digit, value) { changed = true; thisObj.setPositionsInCol((1 << digit), fixed, value); }; } (this, i);
		this.strategyGeneric_SubsetEngine(getter, setter);

		// box
		getter = function (thisObj, fixed) { return function (digit) { return thisObj.getPositionsInBox((1 << digit), fixed); }; } (this, i);
		setter = function (thisObj, fixed) { return function (digit, value) { changed = true; thisObj.setPositionsInBox((1 << digit), fixed, value); }; } (this, i);
		this.strategyGeneric_SubsetEngine(getter, setter);
	}
	return changed;
}

// Call the n-fish strategies on the grid
// this function uses the fact that for rows and cols, naked pairs and n-fish are computationally identical
// Naked pairs expects array format to be:   grid[row][col] = digit;
// N-fish expects array format to be:        grid[digit][row] = col;
//                                       or  grid[digit][col] = row;
SudokuGrid.prototype.strategyFish = function ()
{
	var getter, setter, changed = false;

	console.log('=== N-Fish ===');

	for (var i = 1; i < 512; i <<= 1)
	{
		// row
		getter = function (thisObj, fixed) { return function (row) { return thisObj.getPositionsInRow(fixed, row); }; } (this, i);
		setter = function (thisObj, fixed) { return function (row, value) { changed = true; thisObj.setPositionsInRow(fixed, row, value); }; } (this, i);
		this.strategyGeneric_SubsetEngine(getter, setter);
	
		// column
		getter = function (thisObj, fixed) { return function (column) { return thisObj.getPositionsInCol(fixed, column); }; } (this, i);
		setter = function (thisObj, fixed) { return function (column, value) { changed = true; thisObj.setPositionsInCol(fixed, column, value); }; } (this, i);
		this.strategyGeneric_SubsetEngine(getter, setter);
	}
	return changed;
}
